/* This is a free library */
// Project:  S-dict(t) OI
// Filename: sdict_t.js
// Version:  1.22b
// Author:   Ray Stinger
// Licence:  The MIT License
// Buildate: 30/10/2007

/* begin common section */
function cons (o, l) {
	return [o].concat(l)
}
function rcons (l, o) {
	return l.concat([o])
}
function cdr (l) {
	return l.slice(1)
}
function map (f, l) {
	if (l == false)
		return []
	return cons(f(l[0]), map(f, cdr(l)))
}
/* end common */

/* begin base section */
function SLeaf (vs) {
	var m = vs.length
	var n = 0

	this.next = function () {
		return vs[n++]
	}
	this.get = function (i) {
		n = (i>0?i-1:m+i)+1
		return vs[n-1]
	}
	this.size = function () {
		return m
	}
	this.list = function () {
		return vs
	}
}
function sval (vs) {
	return new SLeaf (vs)
}
var snil = sval([])
/* end base */

/* begin decode section */
function sdict (s) {
	var index = ["~"]
	var root = {}
	var stack = [root]

	function ssub (s, p) {
		var ss = p.exec(s)[0]
		return [ss, s.slice(ss.length)]
	}
	function word (s) {
		return ssub(s, /^[^\s\(\)\[\]\{\}'"`,;]+/)
	}
	function gew (s) {
		return /^[^\s\(\)\[\]'"]+/.exec(s)[0]
	}
	function strim (s) {
		return s.replace(/^\s+?(?=\n)?/,'')
	}
	function error (m, ln) {
		throw Error (m+" in line "+ln)
	}

	function main (s, ln) {
		if (isEOF(s))
			if (!canPop(stack))
				return
			else error("Lack of end quotes", ln)
		if (beLine(s))
			main(cdr(s), ln+1)
		else if (beSpase(s))
			main(strim(s), ln)
		else if (beMain(s))
			tree(cdr(s), ln)
		else if (beEnd(s))
			if (canPop(stack)) {
				stack.pop()
				main(cdr(s), ln)
			}
			else error("To many end quotes", ln)
		else error("Wrong Syntax: "+gew(s), ln)
	}

	function tree (s, ln) {
		if (beLine(s))
			tree(cdr(s), ln+1)
		else if (beSpase(s))
			tree(strim(s), ln)
		else if (beId(s)) {
			var tmp = id(s)
			add_index(stack, tmp[0])
			leaf(tmp[1], ln)
		}
		else error("Not an Id: "+gew(s))
	}

	function leaf (s, ln) {
		if (beLine(s))
			leaf(cdr(s), ln+1)
		else if (beSpase(s))
			leaf(strim(s), ln)
		else if (beMain(s))
			tree(cdr(s), ln)
		else exps(s, ln)
	}

	function exps (s, ln) {
		function iter (s, ln, l) {
			if (beLine(s))
				return iter(cdr(s), ln+1, l)
			if (beSpase(s))
				return iter(strim(s), ln, l)
			if (beEnd(s))
				return [l, cdr(s), ln]
			var rst = exp(s, ln)
			return iter(rst[1], rst[2], rcons(l, rst[0]))
		}

		var tmp = iter(s, ln, [])
		stack[stack.length-2][index.pop()] = 
			make_sval(tmp[0])
		stack.pop()
		main(tmp[1], tmp[2])
	}

	function exp (s, ln) {
		if (beBool(s)) return bool(s, ln)
		if (beNum(s)) return num(s, ln)
		if (beId(s)) return get(s, ln)
		if (beStr(s)) return str(s, ln)
		if (beArr(s)) return arr(s, ln)
		error("Unknown Value: "+gew(s), ln)
	}

	function isEOF (c) {
		return c == ""
	}
	function beLine (c) {
		return c[0] == '\n'
	}
	function beSpase (c) {
		return /\s/.test(c[0])
	}
	function beId (c) {
		return /[^#\.\d\(\)\[\]\{\}'"`,;+-]/.test(c[0])
	}
	function beBool (c) {
		return c[0] == '#'
	}
	function beNum (c) {
		return /[-+\d\.]/.test(c[0])
	}
	function beStr (c) {
		return c[0] == '\'' || c[0] == '\"'
	}
	function beArr (c) {
		return c[0] == '['
	}
	function beNarr (c) {
		return c[0] == ']'
	}
	function beMain (c) {
		return c[0] == '('
	}
	function beEnd (c) {
		return c[0] == ')'
	}

	function bool (s, ln) {
		var tw = word(s)
		var ts = tw[0].toLowerCase()
		switch (ts) {
			case '#t': return [true, tw[1], ln]
			case '#f': return [false, tw[1], ln]
		}
		return [ts, tw[1], ln]
	}
	function num (s, ln) {
		var tw = word(s)
		if (tw[0][1] != 'x' || tw[0][1] != 'X')
			return [Number(tw[0]), tw[1], ln]
		return [tw[0], tw[1], ln]
	}
	function id (s, ln) {
		return rcons(word(s), ln)
	}
	function get (s, ln) {
		var tw = id(s, ln)
		var tv = lookup(stack, tw[0])
		if (tv != undefined && tv instanceof SLeaf)
			return [tv, tw[1], ln]
		return [tw[0], tw[1], ln]
	}
	function str (s, ln) {
		function iter (s, q) {
			try {
				var ts = ssub(s, RegExp("^[^\n]*?"+q))
				var a = /\\*['"]$/.exec(ts[0])
			} catch (e) {
				error("Unclosing Quote: "+
					q+/^[^\n]+/.exec(s)[0], ln)
			}
			if (a == null || a[0].length % 2 == 1)
				return ts
			var tmp = iter(ts[1], q)
			return [ts[0]+tmp[0], tmp[1]]
		}

		var tw = iter(cdr(s), s[0])
		var tv = eval(s[0]+tw[0])
		return [tv, tw[1], ln]
	}
	function arr (s, ln) {
		return arrc(cdr(s), ln)
	}
	function arrc (s, ln) {
		function iter (s, ln, l) {
			if (beLine(s))
				return iter(cdr(s), ln+1, l)
			if (beSpase(s))
				return iter(strim(s), ln, l)
			if (beNarr(s))
				return [l, cdr(s), ln]
			var rst = exp(s, ln)
			if (rst[0] instanceof SLeaf)
				return iter(rst[1], rst[2], 
						l.concat(rst[0].list()))
			return iter(rst[1], rst[2], rcons(l, rst[0]))
		}
		return iter(s, ln, [])
	}
	function comt (s) {
		return s.replace(/^\s*#.*$/mg, '')
	}

	function make_sval (l) {
		function iter (l) {
			if (l == false)
				return []
			if (l[0] instanceof SLeaf)
				return l[0].list().concat(iter(cdr(l)))
			return cons(l[0], iter(cdr(l)))
		}
		return sval(iter(l))
	}
	function add_index (sk, i) {
		index.push(i)
		stack.push({})
		stack[stack.length-2][i] = stack[stack.length-1]
	}
	function canPop (sk) {
		return !(sk.length == 1)
	}
	function lookup (sk, i) {
		function iter (n) {
			if (i in sk[n])
				return sk[n][i]
			if (n == 0)
				return undefined
			return iter(n-1)
		}
		return iter(sk.length-1)
	}
	
	main(comt(s), 1)
	return root
}
/* end decode */

/* begin encode section */
function sdump (t) {
	function isPrim (t) {
		return isBool(t) || isNum(t)
			|| isStr(t) || isArr(t)
	}
	function isBool (t) {
		return typeof t == 'boolean'
			|| t instanceof Boolean
	}
	function isNum (t) {
		return typeof t == 'number'
			|| t instanceof Number
	}
	function isStr (t) {
		return typeof t == 'string'
			|| t instanceof String
	}
	function isArr (t) {
		return t instanceof Array
	}

	function exps (t) {
		return map(exp, t.list()).join(' ')
	}
	function exp (t) {
		if (isBool(t))
			return bool(t)
		if (isNum(t))
			return num(t)
		if (isStr(t))
			return str(t)
		if (isArr(t))
			return arr(t)
		error("Unsupported Value", t)
	}
	function bool (t) {
		return t ? '#t' : '#f'
	}
	function num (t) {
		if (t != NaN && t != Infinity)
			return t.toString()
		error("Not a real number", t)
	}
	function str (t) {
		var tb = {
			'\"':'\\"',
			'\\':'\\\\',
			'\b':'\\b',
			'\f':'\\f',
			'\n':'\\n',
			'\r':'\\r',
			'\t':'\\t',
			'\v':'\\v',
		}
		function iter (s) {
			if (s == "")
				return ''
			if (tb[s[0]] != undefined)
				return tb[s[0]]+iter(cdr(s))
			return s[0]+iter(cdr(s))
		}
		return '"'+iter(t)+'"'
	}
	function arr (t) {
		return '['+arrc(t)+']'
	}
	function arrc (t) {
		return map(exp, t).join(' ')
	}
	function tree (t) {
		function is_id (s) {
			return /^[^\(\)\[\]\{\}'"`,;]*$/.test(s) 
					&& (/[^#\.\d+-]/.test(s[0]))
		}

		var tmp = []
		for (var i in t) {
			if (t.hasOwnProperty(i) && 
					typeof t[i] != 'function') {
				if (is_id(i))
					tmp.push('('+i+' '+sdump(t[i])+')')
				else error("Unuseable Id", i)
			}
		}
		return tmp.join('\n')
	}
	function error (m, t) {
		throw Error (m+': '+t)
	}

	if (isPrim(t))
		return exp(t)
	if (t instanceof SLeaf)
		return exps(t)
	if (t instanceof Object)
		return tree(t)
	error("Unknown Type", t)
}
/* end encode */
